/*
 * @(#)DiagnosticFormatter.java	1.15 07/03/21
 * 
 * Copyright (c) 2007 Sun Microsystems, Inc.  All Rights Reserved.
 * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
 *  
 * This code is free software; you can redistribute it and/or modify it
 * under the terms of the GNU General Public License version 2 only, as
 * published by the Free Software Foundation.  Sun designates this
 * particular file as subject to the "Classpath" exception as provided
 * by Sun in the LICENSE file that accompanied this code.
 *  
 * This code is distributed in the hope that it will be useful, but WITHOUT
 * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
 * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
 * version 2 for more details (a copy is included in the LICENSE file that
 * accompanied this code).
 *  
 * You should have received a copy of the GNU General Public License version
 * 2 along with this work; if not, write to the Free Software Foundation,
 * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
 *  
 * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara,
 * CA 95054 USA or visit www.sun.com if you need additional information or
 * have any questions.
 */

package com.sun.tools.javac.util;

import javax.tools.JavaFileObject;

import com.sun.tools.javac.util.JCDiagnostic.DiagnosticSource;
import com.sun.tools.javac.util.JCDiagnostic.DiagnosticType;

/** 
 * A formatter for diagnostic messages.
 * The formatter will format a diagnostic according to one of two format strings, depending on whether
 * or not the source name and position are set. The format is a printf-like string,
 * with the following special characters:
 * <ul>
 * <li>%b: the base of the source name, or "-" if not set
 * <li>%f: the source name, or "-" if not set
 * <li>%l: the line number of the diagnostic, derived from the character offset if set, or "-" otherwise
 * <li>%c: the column number of the diagnostic, derived from the character offset if set, or "-" otherwise
 * <li>%o: the character offset of the diagnostic if set, or "-" otherwise
 * <li>%p: the prefix for the diagnostic, derived from the diagnostic type
 * <li>%t: the prefix as it normally appears in standard diagnostics. In this case, no prefix is
 *        shown if the type is ERROR and if a source name is set
 * <li>%m: the text or the diagnostic, including any appropriate arguments
 * </ul>
 */
@Version("@(#)DiagnosticFormatter.java	1.15 07/03/21")
public class DiagnosticFormatter {
	private static my.Debug DEBUG=new my.Debug(my.Debug.Log);//我加上的

    /**
     * A format string to be used for diagnostics with a given position.
     */
    protected String posFormat;

    /**
     * A format string to be used for diagnostics regarding classfiles
     */
    protected String classFormat = DEFAULT_CLASS_FORMAT;

    /**
     * A format string to be used for diagnostics without a given position.
     */
    protected String noPosFormat;

    /**
     * A value to indicate whether to output the i18n key and args, instead of 
     * the derived l10n message.
     */
    protected boolean raw;

    /** The context key for the formatter. */
    protected static final Context.Key<DiagnosticFormatter> formatterKey =
	new Context.Key<DiagnosticFormatter>();

    /** Get the DiagnosticFormatter instance for this context. */
    public static DiagnosticFormatter instance(Context context) {
		DiagnosticFormatter instance = context.get(formatterKey);
		if (instance == null)
			instance = new DiagnosticFormatter(context);
		return instance;
    }

    /**
     * Create a formatter based on the supplied options.
     */
    protected DiagnosticFormatter(Context context) {
		Options options = Options.instance(context);
		raw = options.get("rawDiagnostics") != null;
		String fmt = options.get("diags");
		if (fmt != null) {
			int sep = fmt.indexOf('|');
			if (sep == -1)
				posFormat = noPosFormat = fmt;
			else {
				posFormat = fmt.substring(0, sep);
				noPosFormat = fmt.substring(sep + 1);
			}
		}
		else {
			posFormat = DEFAULT_POS_FORMAT;
			noPosFormat = DEFAULT_NO_POS_FORMAT;
		}
    }

    public static final String DEFAULT_POS_FORMAT = "%f:%l: %t%m"; 
    public static final String DEFAULT_CLASS_FORMAT = "%f: %t%m"; 
    public static final String DEFAULT_NO_POS_FORMAT = "%p%m";

    public DiagnosticFormatter() {
		posFormat = DEFAULT_POS_FORMAT;
		noPosFormat = DEFAULT_NO_POS_FORMAT;
		raw = false;
    }

    public DiagnosticFormatter(String pos, String noPos) {
		posFormat = pos;
		noPosFormat = noPos;
		raw = false;
    }

    String format(JCDiagnostic d) {
		return (raw ? format_raw(d) : format_std(d));
    }

    private String format_raw(JCDiagnostic d) {
		DiagnosticSource source = d.getDiagnosticSource();
		int position = d.getIntPosition();

		StringBuilder sb = new StringBuilder();
		if (position == Position.NOPOS)
			sb.append("-");
		else {
			sb.append(source.getName() + ":" + source.getLineNumber(position) + ":" + source.getColumnNumber(position) + ":");
		}
		sb.append(" ");
		sb.append(d.getCode());
		String sep = ": ";
		for (Object arg: d.getArgs()) {
			sb.append(sep);
			if (arg instanceof JCDiagnostic) {
				sb.append('(');
				sb.append(format_raw((JCDiagnostic) arg));
				sb.append(')');
			}
			else if (arg instanceof JavaFileObject)
				sb.append(JavacFileManager.getJavacBaseFileName((JavaFileObject) arg));
			else
				sb.append(arg);
			sep = ", ";
		}
		return sb.toString();
    }

    private String format_std(JCDiagnostic d) {
		DEBUG.P(this,"format_std(1)");
		//DEBUG.P("d="+d);//这会引起无限调用,引起JCDiagnostic.toString()==>format==>format_std
		
		DiagnosticSource source = d.getDiagnosticSource();
		DiagnosticType type = d.getType();
		int position = d.getIntPosition();

		
		String format = noPosFormat;
		if (source != null) {
			if (position != Position.NOPOS) {
				format = posFormat;
			} else if (source.getFile() != null &&
				   source.getFile().getKind() == JavaFileObject.Kind.CLASS) {
				format = classFormat;
			}
		}

		StringBuilder sb = new StringBuilder();

		DEBUG.P("format="+format);

		for (int i = 0; i < format.length(); i++) {
			char c = format.charAt(i);
			if (c == '%' && i < format.length() - 1) {
				c = format.charAt(++i);
				switch (c) {
				case 'b': 
					sb.append(source == null ? "-" : source.getName()); 
					break;

				case 'e': 
					sb.append(position == Position.NOPOS ? "-" : String.valueOf(d.getEndPosition())); 
					break;

				case 'f': 
					sb.append(source == null ? "-" : d.getSourceName()); 
					break;

				case 'l': 
					sb.append(position == Position.NOPOS ? "-" : String.valueOf(d.getLineNumber())); 
					break;

				case 'c':
					sb.append(position == Position.NOPOS ? "-" : String.valueOf(d.getColumnNumber())); 
					break;

				case 'o': 
					sb.append(position == Position.NOPOS ? "-" : String.valueOf(position)); 
					break;

				case 'p': 
					sb.append(d.getPrefix()); 
					break;

				case 's': 
					sb.append(position == Position.NOPOS ? "-" : String.valueOf(d.getStartPosition())); 
					break;

				case 't': {
					boolean usePrefix;
					switch (type) {
					case FRAGMENT:
						usePrefix = false;
						break;

					case ERROR:
						usePrefix = (position == Position.NOPOS);
						break;

					default:
						usePrefix = true;
					}

					if (usePrefix)
						sb.append(d.getPrefix()); 
					break;
				}

				case 'm': 
					sb.append(d.getMessage(null)); 
					break;

				case '_': 
					sb.append(' '); 
					break;

				case '%': 
					sb.append('%'); 
					break;

				default:  
					sb.append(c); 
					break;
				}
			}
			else
				sb.append(c);
		}
		DEBUG.P(0,this,"format_std(1)");
		return sb.toString();
    }
}
